package com.zzyl.nursing.service.impl;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.uuid.IdUtils;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

/**
 * 设备Service业务层处理
 * 
 * @author wjw
 * @date 2025-09-22
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询设备
     * 
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询设备列表
     * 
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return deviceMapper.insert(device);
    }

    /**
     * 修改设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDevice(Device device)
    {
        //1. 先修改数据库
        deviceMapper.updateById(device);
        //2. 再修改华为云
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.withDeviceId(device.getIotId());
        UpdateDevice body = new UpdateDevice();
        body.withDeviceName(device.getDeviceName());
        request.withBody(body);
        UpdateDeviceResponse response = client.updateDevice(request);
        if ((response.getHttpStatusCode()) != 200){
            throw new RuntimeException("华为云修改设备失败");
        }
    }

    /**
     * 批量删除设备
     * 
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除设备信息
     * 
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return deviceMapper.deleteById(id);
    }

    @Override
    public void syncProductList() {
        //1. 从华为云读取产品列表数据
        ListProductsRequest request = new ListProductsRequest();
        request.setLimit(50);
        ListProductsResponse response = client.listProducts(request);
        if(response.getHttpStatusCode() != 200){
            throw new BaseException("物联网接口 - 查询产品，同步失败");
        }
        List<ProductSummary> products = response.getProducts();
        //2. 存储到Redis中
        redisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT_LIST,JSONUtil.toJsonStr(products));
    }

    @Override
    public List<ProductVo> allProduct() {
        //从redis中查询数据
        String jsonStr = String.valueOf(redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST));
        //如果数据为空，则返回一个空集合
        if(StringUtils.isEmpty(jsonStr)){
            return Collections.emptyList();
        }
        //解析数据，并返回
        return JSONUtil.toList(jsonStr, ProductVo.class);
    }

    @Override
    public void addDevice(DeviceDto deviceDto) {
        Device device = new Device();
        //1. 校验设备名称deviceName字段是否重复
        Long count = lambdaQuery().eq(Device::getDeviceName, deviceDto.getDeviceName()).count();
        if (count > 0){
            throw new RuntimeException("设备名称重复");
        }
        //2. 校验标识: NodeId字段是否重复
        Long count1 = lambdaQuery().eq(Device::getNodeId, deviceDto.getNodeId()).count();
        if (count1>0){
            throw new RuntimeException("设备标识重复");
        }
        //3. 校验同一位置是否绑定了相同的产品
            // 这个比较特殊, 需要把与位置相关的所有字段进行判断
        Long count2 = lambdaQuery()
                //绑定的位置
                .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                //绑定的位置类型
                .eq(Device::getLocationType, deviceDto.getLocationType())
                //绑定的物理位置
                .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                //绑定的产品key
                .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                .count();
        if (count2>0){
            throw new RuntimeException("同一位置绑定了相同的产品");
        }
        //4. 注册设备到华为云
            //先注册华为云: 因为iot_id secret have_entrance_guard这些数据必须在华为云上添加设备之后才能获取
            //注意注册华为云设备时,可以传递自己生成的secret,不然华为云返回的是一个加密的secret
        AddDeviceRequest request = new AddDeviceRequest();
        //构造参数
        AddDevice addDevice = new AddDevice();
        addDevice.setNodeId(deviceDto.getNodeId());
        addDevice.setDeviceName(deviceDto.getDeviceName());
        addDevice.setProductId(deviceDto.getProductKey());
        AuthInfo authInfo = new AuthInfo();
        String secret = IdUtil.fastSimpleUUID();
        authInfo.setSecret(secret);
        addDevice.setAuthInfo(authInfo);
        request.setBody(addDevice);
        AddDeviceResponse response = null;
        try {
            response = client.addDevice(request);
            if (response==null){
                throw new RuntimeException("华为云添加设备失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //5. 解析返回数据,补充字段：  iot_id secret have_entrance_guard产品是否包含门禁
        device = BeanUtil.toBean(deviceDto,Device.class);
        device.setIotId(response.getDeviceId());
        device.setSecret(secret);
        device.setHaveEntranceGuard(0);
        //6. 添加到数据库
        save(device);
    }

    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        //1. 从数据库查询数据
        Device device = lambdaQuery()
                .eq(Device::getIotId, iotId)
                .one();
        //拷贝数据
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        //2. 从华为云查询数据
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        //3. 解析数据取出数据
        String status = response.getStatus();
        String activeTime = response.getActiveTime();
        // 解析为Instant（UTC时间）
        Instant instant = Instant.parse(activeTime);
        // 转换为东八区(Asia/Shanghai)的LocalDateTime
        LocalDateTime time = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
        //4. 封装数据返回
        deviceDetailVo.setDeviceStatus(status);
        deviceDetailVo.setActiveTime(time);
        return deviceDetailVo;
    }

    @Override
    public AjaxResult queryServiceProperties(String iotId) {
        //1. 发送请求到华为云获取数据
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        //2. 解析数据
            // 先打印一下看一下响应的数据格式
            // System.out.println(JSONUtil.toJsonStr(response));
        // 因为不同设备的数据模型不一样,所以返回的都是Object类型,实际类型是一个LinkedHashMap<String,Object>; 通过调试看到
//        Object properties = response.getShadow().get(0).getReported().getProperties();
        LinkedHashMap<String,Object> properties = (LinkedHashMap<String, Object>) response.getShadow().get(0).getReported().getProperties();
        String utcEventTime = response.getShadow().get(0).getReported().getEventTime();
        //将时间转换为东八区的时间字符串;
        // 定义输入格式
        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
        // 解析为UTC时间
        LocalDateTime utcDateTime = LocalDateTime.parse(utcEventTime, inputFormatter);
        ZonedDateTime utcZonedDateTime = utcDateTime.atZone(ZoneId.of("UTC"));
        // 转换为东八区(Asia/Shanghai)时间
        ZonedDateTime cstZonedDateTime = utcZonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
        // 定义输出格式，这里使用常用的格式，可根据需要调整
        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 转换为字符串
        String eventTime = cstZonedDateTime.format(outputFormatter);
        //3. 封装数据
        // 把map数据封装进一个List<map>集合中
        List<Map<String, Object>> list = new ArrayList<>();
        properties.forEach((k,v)->{
            HashMap<String, Object> map = new HashMap<>();
            map.put("functionId",k);
            map.put("value",v);
            map.put("eventTime",eventTime);
            list.add(map);
        });
        return AjaxResult.success(list);
    }

    /**
     * 查询产品详情
     * @param p
     * @return
     */
    @Override
    public AjaxResult queryProduct(String productId) {
        //参数校验
        if(StringUtils.isEmpty(productId)){
            throw new BaseException("请输入正确的参数");
        }
        //调用华为云物联网接口
        ShowProductRequest showProductRequest = new ShowProductRequest();
        showProductRequest.setProductId(productId);
        ShowProductResponse response;

        try {
            response = client.showProduct(showProductRequest);
            System.out.println(response);
        } catch (Exception e) {
            throw new BaseException("查询产品详情失败");
        }
        //判断是否存在服务数据
        List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();
        if(CollUtil.isEmpty(serviceCapabilities)){
            return AjaxResult.success(Collections.emptyList());
        }

        return AjaxResult.success(serviceCapabilities);
    }
}
